﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Weazel.Willows.Model
{
	public partial class QueueWriterInterface
	{

		#region Names
		// Out queue names
		public string CanWriteSignalName
		{
			get { return QueuePrefix + LocalName + "_can_write"; }
		}

		public string CanWriteRegisterName
		{
			get { return QueuePrefix + LocalName + "_can_write_register"; }
		}

		public string EnqueueSignalName
		{
			get { return LocalName; }
		}

		public string DoWriteSignalName
		{
			get { return QueuePrefix + LocalName + "_do_write"; }
		}

		public string QueueWriteSignalFlowGraphName
		{
			get { return QueuePrefix + LocalName + "_write"; }
		}

		public string QueueIdleSignalFlowGraphName
		{
			get { return QueuePrefix + LocalName + "_idle"; }
		}

		#endregion

		#region Gezel Expressions
		/// <summary>
		/// Get a constant expression with a given value and type
		/// </summary>
		/// <param name="type">Type of constant</param>
		/// <param name="value">Value of constant</param>
		/// <returns></returns>
		protected Weazel.Gezel.Model.Expression GetConstantExpression(Type type, string value)
		{
			ConstantExpression expression =
				new ConstantExpression(
					TypeRegistry,
					Type, value
				);

			return expression;
		}

		/// <summary>
		/// Get an identifier expression referencing the 'data_out' 
		/// signal of an out queue.
		/// </summary>
		private Weazel.Gezel.Model.Expression dataOutSignalExpression;
		protected Weazel.Gezel.Model.Expression DataOutSignalExpression
		{
			get
			{
				if (dataOutSignalExpression == null)
				{
					dataOutSignalExpression =
						new IdentifierExpression(
							TypeRegistry,
							Type,
							EnqueueSignalName
						);
				}

				return dataOutSignalExpression;
			}
		}

		/// <summary>
		/// Get an identifier expression referencing the 'do_write' 
		/// signal of an out queue.
		/// </summary>
		private Weazel.Gezel.Model.Expression doWriteSignalExpression;
		protected Weazel.Gezel.Model.Expression DoWriteSignalExpression
		{
			get
			{
				if (doWriteSignalExpression == null)
				{
					doWriteSignalExpression =
						new IdentifierExpression(
							TypeRegistry,
							(Type)TypeRegistry["boolean"],
							DoWriteSignalName
						);
				}

				return doWriteSignalExpression;
			}
		}

		/// <summary>
		/// Get an identifier expression referencing the 'can_write' 
		/// signal of an out queue.
		/// </summary>
		private Weazel.Gezel.Model.Expression canWriteSignalExpression;
		protected Weazel.Gezel.Model.Expression CanWriteSignalExpression
		{
			get
			{
				if (canWriteSignalExpression == null)
				{
					canWriteSignalExpression =
						new IdentifierExpression(
							TypeRegistry,
							(Type)TypeRegistry["boolean"],
							CanWriteSignalName
						);
				}

				return canWriteSignalExpression;
			}
		}

		/// <summary>
		/// Get an identifier expression referencing the 'can_write' 
		/// register of an out queue.
		/// </summary>
		private Weazel.Gezel.Model.Expression canWriteRegisterExpression;
		public Weazel.Gezel.Model.Expression CanWriteRegisterExpression
		{
			get
			{
				if (canWriteRegisterExpression == null)
				{
					canWriteRegisterExpression =
						new IdentifierExpression(
							TypeRegistry,
							(Type)TypeRegistry["boolean"],
							CanWriteRegisterName
						);
				}

				return canWriteRegisterExpression;
			}
		}

		/// <summary>
		/// Get an expression assigning the 'can_write' signal value to the
		/// 'can_write' register.
		/// </summary>
		private Weazel.Gezel.Model.Expression assignCanWriteRegisterExpression;
		public Weazel.Gezel.Model.Expression AssignCanWriteRegisterExpression
		{
			get
			{
				if (assignCanWriteRegisterExpression == null)
				{
					assignCanWriteRegisterExpression =
						new Weazel.Gezel.Model.BinaryExpression(
							Weazel.Gezel.Model.ExpressionType.Assignment,
							CanWriteRegisterExpression,
							CanWriteSignalExpression
						);
				}

				return assignCanWriteRegisterExpression;
			}
		}

		/// <summary>
		/// Get an expression assigning the value zero to the 'do_write'
		/// signal of this port
		/// </summary>
		private Weazel.Gezel.Model.Expression assignDoWriteSignalZeroExpression;
		public Weazel.Gezel.Model.Expression AssignDoWriteSignalZeroExpression
		{
			get
			{
				if (assignDoWriteSignalZeroExpression == null)
				{
					assignDoWriteSignalZeroExpression =
						new Weazel.Gezel.Model.BinaryExpression(
							Weazel.Gezel.Model.ExpressionType.Assignment,
							DoWriteSignalExpression,
							GetConstantExpression((Type)TypeRegistry["boolean"], "0")
						);
				}

				return assignDoWriteSignalZeroExpression;
			}
		}

		/// <summary>
		/// Get an expression assigning the value one to the 'do_write'
		/// signal of this port
		/// </summary>
		private Weazel.Gezel.Model.Expression assignDoWriteSignalOneExpression;
		public Weazel.Gezel.Model.Expression AssignDoWriteSignalOneExpression
		{
			get
			{
				if (assignDoWriteSignalOneExpression == null)
				{
					assignDoWriteSignalOneExpression =
						new Weazel.Gezel.Model.BinaryExpression(
							Weazel.Gezel.Model.ExpressionType.Assignment,
							DoWriteSignalExpression,
							GetConstantExpression((Type)TypeRegistry["boolean"], "1")
						);
				}

				return assignDoWriteSignalOneExpression;
			}
		}

		/// <summary>
		/// Get an expression assigning zero to the 'data_out' signal
		/// of this queue.
		/// </summary>
		private Weazel.Gezel.Model.Expression assignDataOutZeroExpression;
		public Weazel.Gezel.Model.Expression AssignDataOutZeroExpression
		{
			get
			{
				if (assignDataOutZeroExpression == null)
				{
					assignDataOutZeroExpression =
						new Weazel.Gezel.Model.BinaryExpression(
							Weazel.Gezel.Model.ExpressionType.Assignment,
							DataOutSignalExpression,
							GetConstantExpression(Type, "0")
						);
				}

				return assignDataOutZeroExpression;
			}
		}

		public Weazel.Gezel.Model.Expression AppendCanWriteRegisterReadyToCondition(Weazel.Gezel.Model.Expression condition)
		{
			Weazel.Gezel.Model.BinaryExpression binExpr =
				new Weazel.Gezel.Model.BinaryExpression(
					Weazel.Gezel.Model.ExpressionType.And,
					condition,
					CanWriteRegisterExpression
					);

			return binExpr;
		}

		public Weazel.Gezel.Model.Expression AppendCanWriteRegisterNotReadyToCondition(Weazel.Gezel.Model.Expression condition)
		{
			Weazel.Gezel.Model.UnaryExpression notExpr =
				new Weazel.Gezel.Model.UnaryExpression(
					Weazel.Gezel.Model.ExpressionType.Not, 
					CanWriteRegisterExpression);

			Weazel.Gezel.Model.BinaryExpression binExpr =
				new Weazel.Gezel.Model.BinaryExpression(
					Weazel.Gezel.Model.ExpressionType.And,
					condition,
					notExpr
					);

			return binExpr;
		}

		#endregion

		public virtual void Refine(RefinedModel.Model model)
		{			
			if (!(Parent is Module))
			{
				throw new NotSupportedException();
			}

			Module module = Parent as Module;

			module.Refine(model);

		  RefinedModel.Datapath datapath = module.Datapath;

			//
			// 'can_write' register
			//

			// Create the 'can_write' register for this queue
			Weazel.Gezel.Model.Register canWriteRegister =
				new Weazel.Gezel.Model.Register(datapath, CanWriteRegisterName, 1, false);

			// Add it to the datapath
			datapath.Registers.Add(canWriteRegister.LocalName, canWriteRegister);

			// The register should continiously be updated. 
			// Add an assignment expression to the 'always' sfg
			datapath.SignalFlowGraphs["always"].Expressions.Add(AssignCanWriteRegisterExpression);

			//
			// write sfg
			//

			// Create the sfg to be scheduled when this queue is being written
			Weazel.Gezel.Model.SignalFlowGraph writeSfg =
				new Weazel.Gezel.Model.SignalFlowGraph(datapath, QueueWriteSignalFlowGraphName);

			// Add an expression assigning one to the 'do_write' port to the sfg
			writeSfg.Expressions.Add(AssignDoWriteSignalOneExpression);

			// add
			datapath.SignalFlowGraphs.Add(writeSfg.LocalName, writeSfg);

			//
			// idle sfg
			//

			// Create the sfg to be scheduled when this queue is not being written
			Weazel.Gezel.Model.SignalFlowGraph idleSfg =
				new Weazel.Gezel.Model.SignalFlowGraph(datapath, QueueIdleSignalFlowGraphName);

			// Add an expression assigning 0 to the 'do_write' port ..
			idleSfg.Expressions.Add(AssignDoWriteSignalZeroExpression);
			idleSfg.Expressions.Add(AssignDataOutZeroExpression);

			// add sfg
			datapath.SignalFlowGraphs.Add(idleSfg.LocalName, idleSfg);
		}
	}
}
